/* 
 * Copyright (c) 2009, Giora Kosoi giorak@gmail.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the project nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Giora Kosoi ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Giora Kosoi BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <signal.h>

#include <sparrow/reactor.h>
#include <sparrow/thread_connector.h>
#include <sparrow/round_robin.h>
#include <sparrow/tcp_listener.h>
#include <sparrow/timed_buffer.h>
#include <sparrow/buffer_expander.h>
#include <sparrow/one_rule_demux.h>
#include <sparrow/null_handler.h>
#include <sparrow/mux.h>
#include <sparrow/annotator.h>
#include <sparrow/http_connection_manager.h>


int main(int argc, char **argv)
{
    printf("starting http server\n");
    timespec tb_timeout;
    tb_timeout.tv_sec = 5;
    tb_timeout.tv_nsec = 1000 * 1000 * 1000;

    Reactor *listeners = new Reactor;
    TcpListener *http_listener = new TcpListener(listeners, "pumpkin 8080");
    RoundRobin *round_robin = new RoundRobin(listeners);
    TimedBuffer *timed_buffer0 = new TimedBuffer(listeners, "4 1 1000000");
    TimedBuffer *timed_buffer1 = new TimedBuffer(listeners, "4 1 1000000");

    // worker 0
    Reactor *worker0 = new Reactor;
    ThreadConnector *thread_connector0 = new ThreadConnector(worker0);
    BufferExpander *buffer_expander0 = new BufferExpander(worker0);
    OneRuleDemux *one_rule_demux0 = new OneRuleDemux(worker0,
            "accepted-by tcp/127.0.0.1:8080 http\n");
    HttpConnectionManager *http_connection_manager0 = new HttpConnectionManager(worker0, "");

    //HttpError *http_error0 = new HttpError(worker0);
    Annotator *worker_annotator0 = new Annotator(worker0, "worker worker0");
    RoundRobin *aux_round_robin0 = new RoundRobin(worker0);
    TimedBuffer *timed_buffer00 = new TimedBuffer(worker0, "4 1 1000000");
    TimedBuffer *timed_buffer01 = new TimedBuffer(worker0, "4 1 1000000");
    ThreadConnector *aux_out_thread_connector00 = new ThreadConnector(worker0);
    ThreadConnector *aux_out_thread_connector10 = new ThreadConnector(worker0);
    //Mux *mux0 = new Mux(worker0);
    //BufferExpander *aux_out_buffer_expander0 = new BufferExpander(worker0);

    // worker 1
    Reactor *worker1 = new Reactor;
    ThreadConnector *thread_connector1 = new ThreadConnector(worker1);
    BufferExpander *buffer_expander1 = new BufferExpander(worker1);
    OneRuleDemux *one_rule_demux1 = new OneRuleDemux(worker1,
            "accepted-by tcp/127.0.0.1:8080 http\n");
    HttpConnectionManager *http_connection_manager1 = new HttpConnectionManager(worker1, "");

    //HttpError *http_error1 = new HttpError(worker1)*;
    Annotator *worker_annotator1 = new Annotator(worker1, "worker worker1");
    RoundRobin *aux_round_robin1 = new RoundRobin(worker1);
    TimedBuffer *timed_buffer10 = new TimedBuffer(worker1, "4 1 1000000");
    TimedBuffer *timed_buffer11 = new TimedBuffer(worker1, "4 1 1000000");
    ThreadConnector *aux_out_thread_connector01 = new ThreadConnector(worker0);
    ThreadConnector *aux_out_thread_connector11 = new ThreadConnector(worker0);
    //Mux *mux1 = new Mux(worker1);
    //BufferExpander *aux_out_buffer_expander1 = new BufferExpander(worker1);


    // aux 0
    Reactor *aux0 = new Reactor;
    ThreadConnector *aux_thread_connector00 = new ThreadConnector(aux0);
    ThreadConnector *aux_thread_connector10 = new ThreadConnector(aux0);
    Mux *aux_mux0 = new Mux(aux0, "2");
    BufferExpander *aux_buffer_expander0 = new BufferExpander(aux0);
    NullHandler *null0 = new NullHandler(aux0);
    //HttpEcho *http_echo0 = new HttpEcho(aux0);
    //OneRuleDemux *aux_one_rule_demux0 = new OneRuleDemux(aux0, 
    //        "worker worker0 0\n"
    //        "worker worker1 1\n");
    //TimedBuffer *aux_out_timed_buffer00 = new TimedBuffer(aux0, "4 1 1000000");
    //TimedBuffer *aux_out_timed_buffer01 = new TimedBuffer(aux0, "4 1 1000000");

 

    // aux 1
    Reactor *aux1 = new Reactor;
    ThreadConnector *aux_thread_connector01 = new ThreadConnector(aux1);
    ThreadConnector *aux_thread_connector11 = new ThreadConnector(aux1);
    Mux *aux_mux1 = new Mux(aux1, "2");
    BufferExpander *aux_buffer_expander1 = new BufferExpander(aux1);
    NullHandler *null1 = new NullHandler(aux1);
    //HttpEcho *http_echo1 = new HttpEcho(aux1);
    //OneRuleDemux *aux_one_rule_demux1 = new OneRuleDemux(aux0, 
    //        "worker worker0 0\n"
    //        "worker worker1 1\n");
    //TimedBuffer *aux_out_timed_buffer10 = new TimedBuffer(aux1, "4 1 1000000");
    //TimedBuffer *aux_out_timed_buffer11 = new TimedBuffer(aux1, "4 1 1000000");

    // TODO verify connections are in the same reactor 

    // listeners
    //Handler::connect_output(listeners, 0, http_listener, 0);
    Handler::connect_output(http_listener, 0, round_robin, 0);
    Handler::connect_output(round_robin, 0, timed_buffer0, 0);
    //Handler::connect_output(round_robin, 1, timed_buffer1, 0);
    
    // worker 0
    Handler::connect_output(timed_buffer0, 0, thread_connector0, 0);
    Handler::connect_output(thread_connector0, 0, buffer_expander0, 0);
    Handler::connect_output(buffer_expander0, 0, one_rule_demux0, 0); 
    Handler::connect_output(one_rule_demux0, "http", http_connection_manager0, "new-connection");
    Handler::connect_output(http_connection_manager0, "backend", worker_annotator0, 0);
    Handler::connect_output(worker_annotator0, 0, aux_round_robin0, 0); 
    Handler::connect_output(aux_round_robin0, 0, timed_buffer00, 0); 
    Handler::connect_output(aux_round_robin0, 1, timed_buffer01, 0); 

    // worker 1
    Handler::connect_output(timed_buffer1, 0, thread_connector1, 0);
    Handler::connect_output(thread_connector1, 0, buffer_expander1, 0);
    Handler::connect_output(buffer_expander1, 0, one_rule_demux1, 0); 
    Handler::connect_output(one_rule_demux1, "http", http_connection_manager1, "new-connection");
    Handler::connect_output(http_connection_manager1, "backend", worker_annotator1, 0);
    Handler::connect_output(worker_annotator1, 0, aux_round_robin1, 0); 
    Handler::connect_output(aux_round_robin1, 0, timed_buffer10, 0); 
    Handler::connect_output(aux_round_robin1, 1, timed_buffer11, 0); 


    // aux 0
    Handler::connect_output(timed_buffer00, 0, aux_thread_connector00, 0); 
    Handler::connect_output(timed_buffer01, 0, aux_thread_connector01, 0); 
    Handler::connect_output(aux_thread_connector00, 0, aux_mux0, 0); 
    Handler::connect_output(aux_thread_connector01, 0, aux_mux0, 1); 
    Handler::connect_output(aux_mux0, 0, aux_buffer_expander0, 0); 
    Handler::connect_output(aux_buffer_expander0, 0, null0, 0); 

    // aux 1
    Handler::connect_output(timed_buffer10, 0, aux_thread_connector10, 0); 
    Handler::connect_output(timed_buffer11, 0, aux_thread_connector11, 0); 
    Handler::connect_output(aux_thread_connector10, 0, aux_mux1, 0); 
    Handler::connect_output(aux_thread_connector11, 0, aux_mux1, 1); 
    Handler::connect_output(aux_mux1, 0, aux_buffer_expander1, 0); 
    Handler::connect_output(aux_buffer_expander1, 0, null1, 0); 

    aux0->start();
    aux1->start();
    worker0->start();
    worker1->start();
    listeners->start();

    sigset_t sigset;
    sigemptyset(&sigset);
    //sigfillset(&sigset);
    sigsuspend(&sigset);

    return 0;
}

